package com.rupertjones.globalcron.web.service;

import com.rupertjones.globalcron.domain.GlobalCronScheduler;
import com.rupertjones.globalcron.domain.Host;
import com.rupertjones.globalcron.domain.HostDAO;
import com.rupertjones.globalcron.domain.JobDescriptor;
import com.rupertjones.globalcron.domain.JobDescriptorDAO;
import com.rupertjones.globalcron.web.model.DisplayableJob;
import org.apache.log4j.Logger;
import org.quartz.JobExecutionContext;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * <p>GlobalCron &copy Rupert Jones 2011</p>
 *
 * @author rup
 */
@Service
@Transactional
public class DefaultJobService implements JobService {

    private static final Logger LOG = Logger.getLogger(DefaultJobService.class);

    @Autowired
    private JobDescriptorDAO jobDescriptorDAO;

    @Autowired
    private HostDAO hostDAO;

    @Autowired
    @Qualifier("quartzScheduler")
    private GlobalCronScheduler scheduler;

    @Override
    public JobDescriptor getJob(int jobId) {
        return jobDescriptorDAO.findById(jobId);
    }

    @Override
    public DisplayableJob getDisplayableJob(int jobId) {
        if (hasEnabledJob(jobId)) {
            JobDescriptor jobDescriptor = getJob(jobId);
            return asDisplayableJob(jobDescriptor);
        }
        return new DisplayableJob(getJob(jobId));
    }

    public boolean hasEnabledJob(int jobId) {
        return getEnabledJob(jobId) != null;
    }

    @Override
    public boolean runNow(JobDescriptor job) {
        return scheduler.executeJob(job.getId());
    }

    @Override
    public Host getCurrentHost() {
        return hostDAO.findByHostName(Host.getCurrentHost());
    }

    @Override
    public Host getHost(int hostId) {
        return hostDAO.findById(hostId);
    }

    @Override
    public Collection<DisplayableJob> getAllJobs() {
        return asDisplayableJobs(jobDescriptorDAO.getAllJobs());
    }

    @Override
    public void refreshJobs() {
        scheduler.adjustForModifications(true);
    }

    @Override
    public void stopAll() {
        LOG.info("Stopping all jobs..");
        Collection<DisplayableJob> enabledJobs = getEnabledJobs();
        for(DisplayableJob job : enabledJobs) {
            LOG.info("Stopping job "  +job.getName());
            JobDescriptor jobDescriptor = job.getJobDescriptor();
            jobDescriptor.setEnabled(false);
            jobDescriptorDAO.upsert(jobDescriptor);
        }
        scheduler.adjustForModifications();
        LOG.info("Completed stopping all jobs.");
    }

    @Override
    public void save(JobDescriptor job) {
        jobDescriptorDAO.upsert(job);
    }

    @Override
    public void save(Host host) {
        hostDAO.upsert(host);
    }

    @Override
    public void delete(JobDescriptor job) {
        jobDescriptorDAO.logicalDelete(job);
        scheduler.deleteJob(job.getId());
    }

    @Override
    public Collection<DisplayableJob> getDisabledJobs(Host host) {
        return asDisplayableJobs(jobDescriptorDAO.getDisabledJobsForHost(host));
    }

    @Override
    public Collection<Host> getHosts() {
        return hostDAO.findAll();
    }

    @Override
    public Collection<DisplayableJob> getEnabledJobs(Host host) {
        return sortByNextFireTime(asDisplayableJobs(jobDescriptorDAO.getEnabledJobsForHost(host)));
    }

    @Override
    public Collection<DisplayableJob> getDisabledJobs() {
        return asDisplayableJobs(jobDescriptorDAO.getDisabledJobs());
    }

    @Override
    public Collection<DisplayableJob> getDisabledJobsForHost() {
        return asDisplayableJobs(jobDescriptorDAO.getDisabledJobsForHost());
    }

    @Override
    public Collection<DisplayableJob> getEnabledJobsForHost() {
        return asDisplayableJobs(jobDescriptorDAO.getEnabledJobsForHost());
    }

    @Override
    public Collection<DisplayableJob> getExecutingJobs() {
        List<DisplayableJob> jobs = new ArrayList<DisplayableJob>();
        List<JobExecutionContext> execing = scheduler.getCurrentlyExecutingJobs();
        for (JobExecutionContext exec : execing) {
            Trigger trigger = exec.getTrigger();
            DisplayableJob job = getDisplayableJob(Integer.parseInt(trigger.getJobName()));
            jobs.add(job);
        }
        return jobs;
    }

    @Override
    public Collection<DisplayableJob> getEnabledJobs() {
        Collection<JobDescriptor> all = jobDescriptorDAO.getEnabledJobs();
        return sortByNextFireTime(asDisplayableJobs(all));
    }

    private Collection<DisplayableJob> sortByNextFireTime(List<DisplayableJob> input) {
        Collections.sort(input, new Comparator<DisplayableJob>() {
            @Override
            public int compare(DisplayableJob o1, DisplayableJob o2) {
                if (o1.getNextFireTimeAsDate() == null || o2.getNextFireTimeAsDate() == null) {
                    return -1;
                }
                return o1.getNextFireTimeAsDate().compareTo(o2.getNextFireTimeAsDate());
            }
        });
        return input;
    }

    private List<DisplayableJob> asDisplayableJobs(Collection<JobDescriptor> jobs) {
        List<DisplayableJob> displayableJobs = new ArrayList<DisplayableJob>();

        for (JobDescriptor job : jobs) {
            displayableJobs.add(asDisplayableJob(job));
        }
        return new ArrayList<DisplayableJob>(displayableJobs);
    }

    private DisplayableJob getEnabledJob(int id) {
        for (DisplayableJob job : getEnabledJobs()) {
            if (job.getId() == id) {
                return job;
            }
        }
        return null;
    }

    private DisplayableJob asDisplayableJob(JobDescriptor jobDescriptor) {
        Trigger trigger = scheduler.getTrigger(jobDescriptor.getId());
        return new DisplayableJob(jobDescriptor, trigger);
    }
}
